#pragma once

#include "readerwriterlocker.h"
#include "IBitmapData.h"
#include "datastore.h"
#include "Utils.h"

template  < typename tAllocator, typename tRwLocker = CReaderWriterLockerSRW, typename tDataStore = FTLib::CDataStore<128>>
class CBitmapData : public FTLib::IBitmapData2
{
private:
	CReaderWriterLockerSRW	rwLocker;
	tDataStore dataStore;
	tAllocator	allocator;
	FTLib::PixelType	pixelType;
	std::uint32_t		width;
	std::uint32_t		height;
	std::uint32_t		pitch;

	std::uint32_t		extraRows;
	std::uint32_t		extraColumns;

	void*	pData;
	std::uint64_t		dataSize;

	std::atomic<int> lockCnt = ATOMIC_VAR_INIT(0);

public:
	static std::shared_ptr<FTLib::IBitmapData2> Create(FTLib::PixelType pixeltype, std::uint32_t width, std::uint32_t height, std::uint32_t pitch = 0, std::uint32_t extraRows = 0, std::uint32_t extraColumns = 0);

	virtual FTLib::PixelType			GetPixelType() const { return this->pixelType; }
	virtual std::uint32_t		GetWidth() const { return this->width; }
	virtual std::uint32_t		GetHeight() const{ return this->height; }
	virtual FTLib::BitmapLockInfo	Lock()
	{
		std::atomic_fetch_add(&this->lockCnt, 1);
		FTLib::BitmapLockInfo bli;
		bli.ptrData = this->pData;
		bli.ptrDataRoi = ((char*)this->pData) + this->extraRows*this->pitch;
		bli.pitch = this->pitch;
		bli.size = this->dataSize;
		return bli;
	}

	virtual void Unlock()
	{
		int lckCnt = std::atomic_fetch_sub(&this->lockCnt, 1);
		if (lckCnt < 1)
		{
			throw std::logic_error("Lock/Unlock-semantic was violated.");
		}
	}


	virtual void lock_write() { return this->rwLocker.lock_write(); }
	virtual void lock_read() { return  this->rwLocker.lock_read(); }
	virtual void unlock_write() { return this->unlock_write(); }
	virtual void unlock_read() { return this->unlock_read(); }

	virtual bool	GetValue(uint16_t key, void* ptrData, uint8_t sizeData, uint8_t* ptrSizeRequired) const
	{
		return this->dataStore.GetValue(key, ptrData, sizeData, ptrSizeRequired);
	}

	virtual FTLib::ITinyDataStore::AddValueRetCode AddOrSetValue(uint16_t key, void* ptrData, uint8_t sizeData)
	{
		return this->dataStore.AddOrSetValue(key, ptrData, sizeData);
	}


	virtual ~CBitmapData()
	{
		this->allocator.Free(this->pData);
	}


	CBitmapData(FTLib::PixelType pixeltype, std::uint32_t width, std::uint32_t height, std::uint32_t pitch, std::uint32_t extraRows, std::uint32_t extraColumns)
	{
		uint64_t size = (height + extraRows * 2ULL) * pitch;
		this->pData = this->allocator.Allocate(size);
		this->dataSize = size;
		this->width = width;
		this->height = height;
		this->pitch = pitch;
		this->extraColumns = extraColumns;
		this->extraRows = extraRows;
		this->pixelType = pixeltype;
	}

private:
	/*static std::uint32_t GetBytesPerPel(FTLib::PixelType pixeltype)
	{
	switch (pixeltype)
	{
	case FTLib::PixelType::GrayFloat32:
	return 4;
	case FTLib::PixelType::ComplexFloat32:
	return 2 * 4;
	case FTLib::PixelType::Gray16:
	return 2;
	case FTLib::PixelType::Gray8:
	return 1;
	case FTLib::PixelType::ComplexDouble64:
	return 2 * 8;
	case FTLib::PixelType::GrayDouble64:
	return 8;
	default:
	throw std::invalid_argument("Cannot determine default pitch.");
	}
	}*/

	static std::uint32_t CalcDefaultPitch(FTLib::PixelType pixeltype, int width)
	{
		return Utils::GetBytesPerPel(pixeltype)*width;
	}
};

#include "bitmapData.hpp"
